package TDALista;

import java.util.Iterator;

import TDALista.InvalidPositionException;

/**
 * Lista doblemente enlazada
 * @author Jordan Holtz
 *  
 * @param <E> tipo generico
 */
public class DoubleLinkedList<E> implements PositionList<E> {

	private int size;
	private NodoD<E> cabeza;
	private NodoD<E> cola;
	
	public DoubleLinkedList(){
		size=0;
		cabeza=cola=null;
	}
	
	@Override
	public int size() {
		return size;
	}

	@Override
	public boolean isEmpty() {
		return size==0;
	}

	@Override
	public Position<E> first(){
		if(isEmpty())
			return null;
		return cabeza;
	}

	@Override
	public Position<E> last(){
		if(isEmpty())
			return null;
		return cola;
	}

	@Override
	public Position<E> next(Position<E> p) throws InvalidPositionException,
			BoundaryViolationException {
		NodoD<E> nv=check(p);
		if(p!=last()){
			return nv.getNsiguiente();
		}
		else{
			throw new BoundaryViolationException("LLEGO AL FINAL, no hay siguiente");
			
		}
	}

	/**
	 * @param p posicion a validar
	 * @return retorna la posicion validada a nodoD
	 * @throws InvalidPositionException en caso de que la posicion pasada sea nula o no sea un nodoD
	 */
	private NodoD<E> check(Position<E> p) throws InvalidPositionException{
		try{
			if(isEmpty())
				throw new InvalidPositionException("lista vacia, la posicion no deberia ser valida!");
			if(p==null)
				throw new InvalidPositionException("posic nula");
			return (NodoD<E>)p;
		}catch(ClassCastException e){
			throw new InvalidPositionException("error en la conversion, tipo no valido");
		}
		
	}

	@Override
	public Position<E> prev(Position<E> p) throws InvalidPositionException,
			BoundaryViolationException {
		NodoD<E> nv=check(p);
		if(p!=first())
			return nv.getNanterior();
		else
			throw new BoundaryViolationException("SOLO HAY UN ELEMENTO");
	}

	@Override
	public void addFirst(E element) {
		NodoD<E> nodonuevo=new NodoD<E>(element);
		if(isEmpty()){
			cabeza=nodonuevo;
			cola=nodonuevo;
		}
		else{
			NodoD<E> naux=cabeza;
			naux.setNanterior(nodonuevo);
			nodonuevo.setNsiguiente(naux);
			cabeza=nodonuevo;
		}
		size++;
	}

	@Override
	public void addLast(E element) {
		NodoD<E> nodonuevo=new NodoD<E>(element);
		if(isEmpty()){
			cabeza=nodonuevo;
			cola=nodonuevo;
		}
		else{
			NodoD<E> naux=cola;
			naux.setNsiguiente(nodonuevo);
			nodonuevo.setNanterior(naux);
			cola=nodonuevo;
		}
		size++;
		
	}

	@Override
	public void addAfter(Position<E> p, E element)
			throws InvalidPositionException {
		NodoD<E> nodopivot=check(p);
		NodoD<E> nodonuevo=new NodoD<E>(element);
	//		aca no tendria q entrar
//		if(isEmpty()){
//			cabeza=nodonuevo;
//			cola=nodonuevo;
//		}
		if(nodopivot==last()){
			addLast(element);
		}
			else{
				nodonuevo.setNsiguiente(nodopivot.getNsiguiente());
				nodopivot.getNsiguiente().setNanterior(nodonuevo);
				nodonuevo.setNanterior(nodopivot);
				nodopivot.setNsiguiente(nodonuevo);
								
			}
			size++;
		
	}

	@Override
	public void addBefore(Position<E> p, E element)
			throws InvalidPositionException {
		NodoD<E> nodopivot=check(p);
		NodoD<E> nodonuevo=new NodoD<E>(element);
	//		aca no tendria q entrar
//		if(isEmpty()){
//			cabeza=nodonuevo;
//			cola=nodonuevo;
//		}
		if(nodopivot==first()){
			addFirst(element);
		}
		else{
			nodonuevo.setNanterior(nodopivot.getNanterior());
			nodopivot.getNanterior().setNsiguiente(nodonuevo);
			nodonuevo.setNsiguiente(nodopivot);
			nodopivot.setNanterior(nodonuevo);
			
		}
		size++;

	}

	@Override
	public E remove(Position<E> p) throws InvalidPositionException {
		NodoD<E> nv=check(p);
		E res=nv.element();
		
		if(size==1){
			cola=cabeza=null;
		}
		if(isEmpty())
			return null;
		else if(size > 1){
				if(p==first()){
						cabeza=nv.getNsiguiente();
						cabeza.setNanterior(null);
				}
				
				else if(p==last()){
						cola=nv.getNanterior();
						cola.setNsiguiente(null);
				}
				else{
					NodoD<E> anterior=nv.getNanterior();
					anterior.setNsiguiente(nv.getNsiguiente());
					nv.getNsiguiente().setNanterior(nv.getNanterior());
					nv.setNanterior(null);
					nv.setNsiguiente(null);
				}
				
     	}
		
		size--;
		return res;
	}

	@Override
	public E set(Position<E> p, E element) throws InvalidPositionException {
		NodoD<E> nv=check(p);
		E res=nv.element();
		nv.setElem(element);
		return res;
	}

	@Override
	public Iterator<E> iterator() {
		return new IteradorListaSE<E>(this);
	}

	@Override
	public Iterable<Position<E>> positions() {
		ListaSE<Position<E>> lres=new ListaSE<Position<E>>();
		if (!isEmpty()) {
	            Position<E> cursor = first();
	            while (true)
	            {
	                lres.addLast(cursor);
	                if (cursor == last())
	                    break;
	                try {
	                    cursor = next(cursor);
	                }
	                catch (InvalidPositionException ipe) {
	                }
	                catch (BoundaryViolationException bve) {
	                }
	            }
		}
		
        return lres;
	}
	
	/**
    * Devuelve la n-esima posicion en la lista, asumiendo que el inicio es el 0
    * @param n Orden de posicion a buscar
    * @return La posicion n-esima si n&#8804;size(), <b>null</b> caso contrario
    */
   public Position<E> nthPosition(int n) {
	   NodoD<E> pointer = cabeza;
	   	int i = 0;
	   	if (n>=size())
	   		return null;
	   	else {
	   		while (i!=n) {
	   			pointer = pointer.getNsiguiente();
	   			i++;
	   		}
	   		return (Position<E>) pointer;
	   	}
	   
   }
   	
   

}
